package sk.ygor.raynor.rebind.service

import com.google.gwt.core.ext.typeinfo._
import com.google.gwt.user.client.rpc.AsyncCallback
import sk.ygor.raynor.client.json.JSONSerializationException

trait JSONServiceAnalysis {

  val CONFIGURATION_METHODS = List("getInvoker", "getTransformer");

  case class ServiceMethod(
          name: String,
          parameterTypes: List[Tuple2[String,JType]],
          returnType: JType
          )

  def analyzeServiceMethods(jsonServiceClassType: JClassType): List[ServiceMethod] =
    List.fromArray(jsonServiceClassType.getMethods()).filter(!isConfigurationMethod(_)).map(analyzeServiceMethod _)

  def isConfigurationMethod(method:JMethod) = CONFIGURATION_METHODS.contains(method.getName)

  def analyzeServiceMethod(jMethod: JMethod): ServiceMethod = {
    if (jMethod.getReturnType() != JPrimitiveType.VOID) {
      throw new JSONSerializationException("Service methods must have VOID return type. Found: " +
              jMethod.getReturnType());
    }
    val returnType = hasTypedAsyncCallback(jMethod);
    if (returnType == null) {
      throw new JSONSerializationException("Service methods must have last parameter as " +
              "parametrized AsyncCallback<T>: " + jMethod.getName);
    }
    val params = List.fromArray(jMethod.getParameters()).dropRight(1)
    val parameterTypes = params.map(p => (p.getName, p.getType))
    new ServiceMethod(jMethod.getName(), parameterTypes, returnType)
  }


  def hasTypedAsyncCallback(jMethod: JMethod): JType = {
    val paramCount = jMethod.getParameters().length;
    if (paramCount == 0) {
      return null;
    }
    val lastType = jMethod.getParameters()(paramCount - 1).getType();
    if (lastType.getQualifiedSourceName() != classOf[AsyncCallback[_]].getName()) {
      return null;
    }
    val jParameterizedType = lastType.isParameterized();
    if (jParameterizedType == null) {
      return null;
    }
    return jParameterizedType.getTypeArgs()(0)
  }

}
